<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */
namespace App\Controller;

use App\Entity\Result;
use App\Model\Device;
use App\Model\DevicePublish;
use App\Model\Normal;
use App\Service\CallbackMqttService;
use App\Service\DeviceMqttService;
use App\Service\MqttClientService;
use App\Utils\Helper;
use App\Utils\Logger;
use http\Client\Curl\User;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Annotation\GetMapping;
use Hyperf\HttpServer\Annotation\Controller;
use Simps\MQTT\Client;
use Swoole\Coroutine;


/**
 * Class IndexController
 *
 * @package App\Controller
 * @Controller()
 */
class IndexController extends AbstractController
{

    const CRC8_TABLE = [
        0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
        0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
        0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
        0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
        0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
        0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
        0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
        0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
        0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
        0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
        0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
        0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
        0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
        0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
        0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
        0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
    ];
    /**
     * @Inject
     * @var DeviceMqttService
     */
    protected DeviceMqttService $deviceMqttService;

    protected $callbackMqttService;

    public function __construct(CallbackMqttService $callbackMqttService)
    {
        $this->callbackMqttService = $callbackMqttService;
    }

    public function index()
    {
        $user = $this->request->input('user', 'Hyperf');
        $method = $this->request->getMethod();

        return [
            'method' => $method,
            'message' => "Hello {$user}.",
        ];
    }

    public function crc8Calculate($dataBytes) {
        $crc = 0;
        for ($i = 0; $i < strlen($dataBytes); $i++) {
            $byte = ord($dataBytes[$i]); // 假设输入是 ASCII 字符
            $crc ^= $byte;
            $crc = self::CRC8_TABLE[$crc];
        }
        return $crc;
    }



    /**查询状态
     * @return string
     */
    public function statusQuery()
    {
        $mac = $this->request->input('mac');
        $type = $this->request->input('type','prod');
        if($type == 'test'){
            $cmd = $mac . "51".'02';
            $dataBytes = hex2bin($cmd);
            $crc8 = $this->crc8Calculate($dataBytes);
            // 格式化校验码
            $crcHex = sprintf('%02X', $crc8);
            $cmd.=$crcHex;
            return Result::success(['cmd'=>$cmd]);
        }
        $token = $this->request->input('token');
 //       $user = \App\Model\User::where('token',$token)->first();
        $cache = $this->validateToken($token);
        if(!$cache){
            return Result::error(Result::TOKEN_WRONG);
        }
        $user = \App\Model\User::where('id',$cache['user_id'])->first();
        if(!$user){
            return Result::error(Result::USER_WRONG);
        }
        if($user->type == 2 && $user->call_count <= $user->call_count_used){
            return Result::error(Result::CALL_API_NUM_EMPTY);
        }
        try{
        $device = Device::where('mac', $mac)->with('user')->first();
            if ($device->clientId) {
                # 下发查询命令
                $cmd = $device->mac . "51".'02';
                $dataBytes = hex2bin($cmd);
                $crc8 = $this->crc8Calculate($dataBytes);
                // 格式化校验码
                $crcHex = sprintf('%02X', $crc8);
                $cmd.=$crcHex;
                $this->sendCmd($device->id, $device->clientId, $cmd);
                if($device->user && $user->type == 2){
                    Helper::usedAdd($user->id,$mac);
                }
            }
        }catch (\Exception $e){
            return Result::error(403,$e->getMessage().$e->getTraceAsString());
        }
        return Result::success();
    }

    /**重启
     * @return string
     */
    public function restart()
    {
        $mac = $this->request->input('mac');
        $token = $this->request->input('token');
//        $user = \App\Model\User::where('token',$token)->first();
        $cache = $this->validateToken($token);
        if(!$cache){
            return Result::error(Result::TOKEN_WRONG);
        }
        $user = \App\Model\User::where('id',$cache['user_id'])->first();
        if(!$user){
            return Result::error(Result::USER_WRONG);
        }
        if($user->type == 2 && $user->call_count <= $user->call_count_used){
            return Result::error(Result::CALL_API_NUM_EMPTY);
        }
        $device = Device::where('mac', $mac)->with('user')->first();
        if ($device->clientId) {
                # 下发合闸命令
            $cmd = $device->mac . "52" .'02';
            $dataBytes = hex2bin($cmd);
            $crc8 = $this->crc8Calculate($dataBytes);
            // 格式化校验码
            $crcHex = sprintf('%02X', $crc8);
            $cmd.=$crcHex;
            $this->sendCmd($device->id, $device->clientId, $cmd);
            if($device->user && $user->type == 2){
                Helper::usedAdd($user->id,$mac);
            }
        }
        return Result::success();
    }




//修改计量模式表速等
    public function setParams()
    {
        $mac = $this->request->input('mac');
        $token = $this->request->input('token');
//        $user = \App\Model\User::where('token',$token)->first();
        $cache = $this->validateToken($token);
        if(!$cache){
            return Result::error(Result::TOKEN_WRONG);
        }
        $user = \App\Model\User::where('id',$cache['user_id'])->first();
        if(!$user){
            return Result::error(Result::USER_WRONG);
        }
        if($user->type == 2 && $user->call_count <= $user->call_count_used){
            return Result::error(Result::CALL_API_NUM_EMPTY);
        }
        $device = Device::where('mac', $mac)->with('user')->first();
        $option = $this->request->input('option');
        $value = $this->request->input('value',0);
        if(!$value || !$option){
            return Result::error(Result::PARAMS_WRONG);
        }
        if ($device->clientId) {
            # 下发调表
            $value = strtoupper(dechex((int)$value));
            $cmd = $device->mac . "55" . "02". $option.$this->deviceMqttService->toLen($value, 8);
            $dataBytes = hex2bin($cmd);
            $crc8 = $this->crc8Calculate($dataBytes);
            // 格式化校验码
            $crcHex = sprintf('%02X', $crc8);
            $cmd.=$crcHex;
            $this->sendCmd($device->id, $device->clientId, $cmd);
            # 保存
            $cmd = $device->mac . "55" . "02".'FE'.'00000000';
            $dataBytes = hex2bin($cmd);
            $crc8 = $this->crc8Calculate($dataBytes);
            // 格式化校验码
            $crcHex = sprintf('%02X', $crc8);
            $cmd.=$crcHex;
            $this->sendCmd($device->id, $device->clientId, $cmd);
            if($device->user && $user->type == 2){
                Helper::usedAdd($user->id,$mac);
            }
       }
        return Result::success();
    }

    public function queryParams(){
        $mac = $this->request->input('mac');
        $token = $this->request->input('token');
        $type = $this->request->input('type','prod');
//        $user = \App\Model\User::where('token',$token)->first();
        $cache = $this->validateToken($token);
        if(!$cache){
            return Result::error(Result::TOKEN_WRONG);
        }
        $user = \App\Model\User::where('id',$cache['user_id'])->first();
        if(!$user){
            return Result::error(Result::USER_WRONG);
        }
        if($user->type == 2 && $user->call_count <= $user->call_count_used){
            return Result::error(Result::CALL_API_NUM_EMPTY);
        }
        $device = Device::where('mac', $mac)->with('user')->first();
        $option = $this->request->input('option');
        if($type == 'test'){
            $cmd = $mac . "55" . "02". $option.'00000000';
            $dataBytes = hex2bin($cmd);
            $crc8 = $this->crc8Calculate($dataBytes);
            // 格式化校验码
            $crcHex = sprintf('%02X', $crc8);
            $cmd.=$crcHex;
            return Result::success(['cmd'=>$cmd]);
        }
        if ($device->clientId) {
            # 下发调表
            $cmd = $device->mac . "55" . "02". $option.'00000000';
            $dataBytes = hex2bin($cmd);
            $crc8 = $this->crc8Calculate($dataBytes);
            // 格式化校验码
            $crcHex = sprintf('%02X', $crc8);
            $cmd.=$crcHex;
            $this->sendCmd($device->id, $device->clientId, $cmd);
            if($device->user && $user->type == 2){
                Helper::usedAdd($user->id,$mac);
            }
        }
        return Result::success();
    }
    /**调整ip地址
     * @return string
     */
    public function setMqtt(){
        $data = $this->request->post();
        $token = $this->request->input('token');
//        $user = \App\Model\User::where('token',$token)->first();
        $cache = $this->validateToken($token);
        if(!$cache){
            return Result::error(Result::TOKEN_WRONG);
        }
        $user = \App\Model\User::where('id',$cache['user_id'])->first();
        if(!$user){
            return Result::error(Result::USER_WRONG);
        }
        if($user->type == 2 && $user->call_count <= $user->call_count_used){
            return Result::error(Result::CALL_API_NUM_EMPTY);
        }
        $device = Device::where('mac', $data['mac'])->with('user')->first();
        if ($device->clientId) {
            # 下发查询命令
            $url = bin2hex($this->strPadUtf8($data['url'], 32));
            $port = $this->uintToBigEndian($data['port']);
            $name = $this->strPadUtf8($data['name'], 16);
            $password = $this->strPadUtf8($data['password'], 16); // 16字节
            $path = $this->strPadUtf8($data['path'], 32);
            $cmd = $device->mac . "43".$url.$port.$name.$password.$path;
            $dataBytes = hex2bin($cmd);
            $crc8 = $this->crc8Calculate($dataBytes);
            // 格式化校验码
            $crcHex = sprintf('%02X', $crc8);
            $cmd.=$crcHex;
            $this->sendCmd($device->id, $device->clientId, $cmd);
            if($device->user && $user->type == 2){
                Helper::usedAdd($user->id,$data['mac']);
            }
        }
        return Result::success();
    }

    public function setElec(){
        try{
            $mac = $this->request->input('mac');
            $token = $this->request->input('token');
            $cache = $this->validateToken($token);
            if(!$cache){
                return Result::error(Result::TOKEN_WRONG);
            }
            $user = \App\Model\User::where('id',$cache['user_id'])->first();
            if(!$user){
                return Result::error(Result::USER_WRONG);
            }
            if($user->type == 2 && $user->call_count <= $user->call_count_used){
                return Result::error(Result::CALL_API_NUM_EMPTY);
            }
        $value = $this->request->input('value');
        if(empty($value)){
            $value = 0;
        }
        $device = Device::where('mac', $mac)->with('user')->first();
        $value = $value*1000;
            if ($device->clientId) {
                # 下发查询命令
                $cmd = $device->mac . "45".'02'. $this->deviceMqttService->intTo4ByteSigned($value);
                $dataBytes = hex2bin($cmd);
                $crc8 = $this->crc8Calculate($dataBytes);
                // 格式化校验码
                $crcHex = sprintf('%02X', $crc8);
                $cmd.=$crcHex;
                $this->sendCmd($device->id, $device->clientId, $cmd);
                if($device->user && $user->type == 2){
                    Helper::usedAdd($user->id,$mac);
                }
            }
        }catch (\Exception $e){
            Normal::logger('set-elec--error:'.$e->getMessage().$e->getTraceAsString(),'order-queue');
        }
        return Result::success();
    }


    public function setHeart(){
        $mac = $this->request->input('mac');
        $seconds = $this->request->input('seconds');
        $token = $this->request->input('token');
        $device = Device::where('mac', $mac)->with('user')->first();
        $cache = $this->validateToken($token);
        if(!$cache){
            return Result::error(Result::TOKEN_WRONG);
        }
        $user = \App\Model\User::where('id',$cache['user_id'])->first();
        if(!$user){
            return Result::error(Result::USER_WRONG);
        }
        $device->heart_hycle = $seconds;
        $device->save();
        return Result::success();
    }

    //设置开合闸
    public function setStatus(){
        $mac = $this->request->input('mac');
        $option = $this->request->input('option');
        $token = $this->request->input('token');

        $cmd1 = $mac . "57".'02'.$option;
        $dataBytes = hex2bin($cmd1);
        $crc8 = $this->crc8Calculate($dataBytes);
        $data['crc8'] = $crc8;
        // 格式化校验码
        $crcHex = sprintf('%02X', $crc8);
        $cmd1.=$crcHex;
//        $this->sendCmd(999, 'cesjosjoj', $cmd);
        if(!$token){
            return Result::error(Result::TOKEN_WRONG);
        }
//        $user = \App\Model\User::where('token',$token)->first();
        $cache = $this->validateToken($token);
        if(!$cache){
            return Result::error(Result::TOKEN_WRONG);
        }
        $user = \App\Model\User::where('id',$cache['user_id'])->first();
        if(!$user){
            return Result::error(Result::USER_WRONG);
        }
        if($user->type == 2 && $user->call_count <= $user->call_count_used){
            return Result::error(Result::CALL_API_NUM_EMPTY);
        }
        $device = Device::where('mac', $mac)->with('user')->first();
        if ($device->clientId) {
            # 下发查询命令
            $cmd = $device->mac . "57".'02'.$option;
            $dataBytes = hex2bin($cmd);
            $crc8 = $this->crc8Calculate($dataBytes);
            // 格式化校验码
            $crcHex = sprintf('%02X', $crc8);
            $cmd.=$crcHex;
            $this->sendCmd($device->id, $device->clientId, $cmd);

            if($device->user && $user->type == 2){
                Helper::usedAdd($user->id,$mac);
            }
        }
        $data['cmd'] = $cmd1;
        return Result::success($data);
    }



   public function sendCmd1($deviceId, $topic, $cmd, $qos = 1)
    {
        DevicePublish::create([
            'device_id' => $deviceId,
            'topic' => $topic,
            'data' => $this->deviceMqttService->returnData1($cmd),
            'qos' => $qos
        ]);
    }

    public function sendCmd($deviceId, $topic, $cmd, $qos = 1)
    {
        DevicePublish::create([
            'device_id' => $deviceId,
            'topic' => $topic,
            'is_send' =>0,
//          'data' => $this->deviceMqttService->returnData($cmd),
            'data'=>$cmd,
            'qos' => $qos
        ]);
    }

    public function strPadUtf8($str, $length) {
        return mb_convert_encoding($str . str_repeat("\x00", $length), 'UTF-8', 'UTF-8');
    }

    public function uintToBigEndian($num, $bytes = 2) {
        $result = [];
        for ($i = 0; $i < $bytes; $i++) {
            $result[] = sprintf("%02X", ($num >> ($i * 8)) & 0xFF);
        }
        return implode($result, '');
    }

    public function getToken()
    {
        $account = $this->request->input('account');
        $password = $this->request->input('password');

        if (empty($account) || empty($password)) {
            return Result::error(Result::PARAMS_WRONG, '账号或密码不能为空');
        }

        // 验证账号密码
        $user = \App\Model\User::where('email', $account)->first();

        if (!$user || !password_verify($password, $user->password)) {
            return Result::error(Result::USER_WRONG, '账号或密码错误');
        }

        // 生成 token
        $token = md5(uniqid($account, true));
        $expireTime = 7200; // 2小时过期
        // 使用 Redis 存储 token 和用户信息
        $redis = \Hyperf\Utils\ApplicationContext::getContainer()->get(\Hyperf\Redis\Redis::class);
        $key = 'user_token:' . $token;
        $userData = [
            'user_id' => $user->id,
            'account' => $user->email,
            'created_at' => time()
        ];

        // 设置过期时间
        $redis->setex($key, $expireTime, json_encode($userData));

        // 更新用户的 token 字段
        $user->token = $token;
        $user->save();
        return Result::success([
            'token' => $token,
            'expire_in' => $expireTime
        ]);
    }


    public function refreshToken()
    {
        $token = $this->request->input('token');
        $redis = \Hyperf\Utils\ApplicationContext::getContainer()->get(\Hyperf\Redis\Redis::class);
        $key = 'user_token:' . $token;
        $userData = $redis->get($key);

        if (!$userData) {
            return false; // 这里应该返回 Result::error()
        }
        $expireTime = 7200;
        $redis->expire($key, $expireTime);
        return Result::success([ // 缺少 return
            'token' => $token,
            'expire_in' => $expireTime
        ]);
    }


    protected function validateToken(string $token)
    {
        $redis = \Hyperf\Utils\ApplicationContext::getContainer()->get(\Hyperf\Redis\Redis::class);
        $key = 'user_token:' . $token;
        $userData = $redis->get($key);
        if (!$userData) {
            return false;
        }
        return json_decode($userData, true);
    }



}
